自定义生成函数

通过使用生成处理程序函数可以自定义从客户端上的预制件生成衍生对象的默认行为。通过这种方式,您可以完全控制如何生成对象以及如何生成对象。您可以注册函数以使用ClientScene.RegisterSpawnHandler生成和取消生成客户端对象。服务器直接创建对象,然后通过此功能在客户端产生它们。该函数获取对象的assetID和两个函数委托,一个用于处理客户端上的创建对象,另一个用于处理客户端上的销毁对象。assetID可以是动态的,也可以是要生成的预制对象上的assetID(如果有的话)。

spawn / un-spawner需要有这个对象签名,这是在高级API中定义的。

    // Handles requests to spawn objects on the client
    public delegate GameObject SpawnDelegate(Vector3 position, NetworkHash128 assetId);

    // Handles requests to unspawn objects on the client
    public delegate void UnSpawnDelegate(GameObject spawned);

传递给spawn函数的assetID可以在NetworkIdentity.assetId中找到,用于预制,它自动填充。动态assetID的注册处理方式如下:

    // generate a new unique assetId 
    NetworkHash128 creatureAssetId = NetworkHash128.Parse("e2656f");

    // register handlers for the new assetId
    ClientScene.RegisterSpawnHandler(creatureAssetId, SpawnCreature, UnSpawnCreature);

    // get assetId on an existing prefab
    NetworkHash128 bulletAssetId = bulletPrefab.GetComponent<NetworkIdentity>().assetId;

    // register handlers for an existing prefab you'd like to custom spawn
    ClientScene.RegisterSpawnHandler(bulletAssetId, SpawnBullet, UnSpawnBullet);

    // spawn a bullet - SpawnBullet will be called on client.
    NetworkServer.Spawn(gameObject, creatureAssetId);

生成函数本身是通过委托签名实现的,这里是子弹生成器,但SpawnCreature看起来是相同的,但有不同的生成逻辑

    public GameObject SpawnBullet(Vector3 position, NetworkHash128 assetId)
    {
        return (GameObject)Instantiate(m_BulletPrefab, position, Quaternion.identity);
    }
    public void UnSpawnBullet(GameObject spawned)
    {
        Destroy(spawned);
    }

使用自定义spawn函数时,有时可以在不破坏对象的情况下取消对象。这可以通过调用NetworkServer.UnSpawn完成。这会导致将消息发送给客户端以取消生成该对象,以便在客户端上调用定制的未生成函数。调用此函数时,对象不会被销毁。

请注意,在主机上,不会为本地客户端生成对象,因为它们已经存在于服务器上。所以没有派生处理函数会被调用。

使用自定义spawn处理程序设置对象池

这里是一个例子,你可以用自定义的spawn处理程序设置一个非常简单的对象池系统。

    using UnityEngine;
    using UnityEngine.Networking;
    using System.Collections;

    public class SpawnManager : MonoBehaviour
    {
        public int m_ObjectPoolSize = 5;
        public GameObject m_Prefab;
        public GameObject[] m_Pool;

        public NetworkHash128 assetId { get; set; }

        public delegate GameObject SpawnDelegate(Vector3 position, NetworkHash128 assetId);
        public delegate void UnSpawnDelegate(GameObject spawned);

        void Start()
        {
            assetId = m_Prefab.GetComponent<NetworkIdentity> ().assetId;
            m_Pool = new GameObject[m_ObjectPoolSize];
            for (int i = 0; i < m_ObjectPoolSize; ++i)
            {
                m_Pool[i] = (GameObject)Instantiate(m_Prefab, Vector3.zero, Quaternion.identity);
                m_Pool[i].name = "PoolObject" + i;
                m_Pool[i].SetActive(false);
            }

            ClientScene.RegisterSpawnHandler(assetId, SpawnObject, UnSpawnObject);
        }

        public GameObject GetFromPool(Vector3 position)
        {
            foreach (var obj in m_Pool)
            {
                if (!obj.activeInHierarchy)
                {
                    Debug.Log("Activating object " + obj.name + " at " + position);
                    obj.transform.position = position;
                    obj.SetActive (true);
                    return obj;
                }
            }
            Debug.LogError ("Could not grab object from pool, nothing available");
            return null;
        }

        public GameObject SpawnObject(Vector3 position, NetworkHash128 assetId)
        {
            return GetFromPool(position);
        }

        public void UnSpawnObject(GameObject spawned)
        {
            Debug.Log ("Re-pooling object " + spawned.name);
            spawned.SetActive (false);
        }
    }

要使用此管理器,请执行以下操作

    • 这适用于像“ 入门指南”中那样的场景。
    • 创建一个名为“SpawnManager”的新空游戏对象
    • 为上面的代码创建一个SpawnManager脚本,并将其添加到新的SpawnManager对象中
    • 将想要多次产生的预制件拖到预制字段并设置大小(默认值为5)。
    • 在玩家移动脚本中设置对spawn管理器的引用
    SpawnManager spawnManager;

    void Start()
    {
        spawnManager = GameObject.Find("SpawnManager").GetComponent<SpawnManager> ();
    }
    • 玩家逻辑可以包含这样的东西来移动和发射子弹
    void Update()
    {
        if (!isLocalPlayer)
            return;

        var x = Input.GetAxis("Horizontal")*0.1f;
        var z = Input.GetAxis("Vertical")*0.1f;

        transform.Translate(x, 0, z);

        if (Input.GetKeyDown(KeyCode.Space))
        {
            // Command function is called on the client, but invoked on the server
            CmdFire();
        }
    }
    • 在播放器的启动逻辑中,使其使用对象池
    [Command]
    void CmdFire()
    {
        // Set up bullet on server
        var bullet = spawnManager.GetFromPool(transform.position + transform.forward);  
        bullet.GetComponent<Rigidbody>().velocity = transform.forward*4;

        // spawn bullet on client, custom spawn handler will be called
        NetworkServer.Spawn(bullet, spawnManager.assetId);

        // when the bullet is destroyed on the server it is automatically destroyed on clients
        StartCoroutine (Destroy (bullet, 2.0f));
    }

    public IEnumerator Destroy(GameObject go, float timer)
    {
        yield return new WaitForSeconds (timer);
        spawnManager.UnSpawnObject(go);
        NetworkServer.UnSpawn(go);
    }

自动销毁显示对象如何返回到池中,并在再次启动时重新使用。

🔚